]>
Commit | Line | Data |
---|---|---|
5200836d RBR |
1 | #import "BBSearchObjectView.h" |
2 | ||
3 | @implementation BBSearchObjectView | |
4 | + (Class)cellClass { return [BBObjectCell class]; } | |
5 | ||
6 | @end | |
7 | ||
8 | @implementation BBCollectingSearchObjectView | |
9 | + (Class)cellClass { return [BBObjectCell class]; } | |
10 | ||
11 | - (void)drawRect:(NSRect)rect { | |
12 | NSRect frame = [self frame]; | |
13 | NSInteger count = [collection count]; | |
14 | if (![self currentEditor] && count) { | |
15 | frame.origin = NSZeroPoint; | |
16 | [[self cell] drawWithFrame:frame inView:self]; | |
17 | NSInteger i; | |
18 | CGFloat iconSize = collectionSpace?collectionSpace:16; | |
19 | CGFloat opacity = collecting?1.0:0.75; | |
20 | QSObject *object; | |
21 | CGFloat totalWidth = iconSize + 2; | |
22 | for (i = 0; i<count; i++) { | |
23 | object = [collection objectAtIndex:i]; | |
24 | NSImage *icon = [object icon]; | |
25 | [icon setSize:QSSize16]; | |
26 | [icon drawInRect:NSMakeRect(frame.size.width-totalWidth*(count-i), frame.origin.y+2, iconSize, iconSize) fromRect:rectFromSize([icon size]) operation:NSCompositingOperationSourceOver fraction:opacity]; | |
27 | } | |
28 | } else { | |
29 | [super drawRect:rect]; | |
30 | } | |
31 | } | |
32 | ||
33 | - (NSRect)textEditorFrame { | |
34 | NSRect titleFrame = [self frame]; | |
35 | NSRect editorFrame = NSInsetRect(titleFrame, 8, 8); | |
36 | editorFrame.origin = NSMakePoint(8, 8); | |
37 | editorFrame = NSIntegralRect(editorFrame); | |
38 | return editorFrame; | |
39 | } | |
40 | ||
41 | ||
42 | @end | |
43 | ||
44 | @implementation BBObjectCell | |
45 | ||
46 | - (NSCellImagePosition)preferredImagePosition { | |
47 | return NSImageAbove; | |
48 | } | |
49 | ||
50 | - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView { | |
51 | BOOL isFirstResponder = [[controlView window] firstResponder] == controlView && ![controlView isKindOfClass:[NSTableView class]]; | |
52 | BOOL dropTarget = ([self isHighlighted] && ([self highlightsBy] & NSChangeBackgroundCellMask) && ![self isBezeled]); | |
53 | ||
54 | NSColor *fillColor; | |
55 | NSColor *strokeColor = [NSColor clearColor]; | |
56 | ||
57 | if (isFirstResponder) { | |
58 | fillColor = [self highlightColor]; | |
59 | } else { | |
60 | fillColor = [self backgroundColor]; | |
61 | } | |
62 | ||
63 | if (dropTarget) { | |
64 | fillColor = [NSColor colorWithRed:0.77 green:0.91 blue:0.96 alpha:1]; | |
65 | } | |
66 | ||
67 | [fillColor setFill]; | |
68 | [strokeColor setStroke]; | |
69 | ||
70 | NSBezierPath *roundRect = [NSBezierPath bezierPath]; | |
71 | [roundRect appendBezierPathWithRoundedRectangle:cellFrame withRadius:cellRadiusFactor]; | |
72 | [roundRect fill]; | |
73 | ||
74 | [self drawInteriorWithFrame:[self drawingRectForBounds:cellFrame] inView:controlView]; | |
75 | } | |
76 | ||
77 | - (NSRect)titleRectForBounds:(NSRect)theRect | |
78 | { | |
79 | NSRect rect = theRect; | |
80 | rect = NSOffsetRect(rect, 0, -4); | |
81 | return [super titleRectForBounds: rect]; | |
82 | } | |
83 | ||
84 | - (void)drawTextForObject:(QSObject *)drawObject withFrame:(NSRect)cellFrame inView:(NSView *)controlView { | |
85 | if ([self imagePosition] == NSImageOnly) return; | |
86 | ||
87 | NSString *abbrString = nil; | |
88 | if ([controlView respondsToSelector:@selector(matchedString)]) | |
89 | abbrString = [(QSSearchObjectView *)controlView matchedString]; | |
90 | ||
91 | NSString *nameString = nil; | |
92 | NSIndexSet *hitMask = nil; | |
93 | ||
94 | id ranker = [drawObject ranker]; | |
95 | if (ranker && abbrString) | |
96 | nameString = [ranker matchedStringForAbbreviation:abbrString hitmask:&hitMask inContext:nil]; | |
97 | ||
98 | if (!nameString) | |
99 | nameString = [drawObject displayName]; | |
100 | ||
101 | BOOL rankedStringIsName = [nameString isEqualToString:[drawObject displayName]] || nameString == nil; | |
102 | if (!nameString) { | |
103 | // fall back to the identifier if no reasonable name can be found | |
104 | nameString = [drawObject identifier]; | |
105 | } | |
106 | if (!nameString) { | |
107 | // Couldn't find anything sensible to use for the name, fallback to avoid a crash | |
108 | nameString = @"Unknown"; | |
109 | } | |
110 | ||
111 | BOOL useAlternateColor = [controlView isKindOfClass:[NSTableView class]] && [(NSTableView *)controlView isRowSelected:[(NSTableView *)controlView rowAtPoint:cellFrame.origin]]; | |
112 | NSColor *mainColor = (textColor ? textColor : (useAlternateColor ? [NSColor alternateSelectedControlTextColor] : [NSColor controlTextColor])); | |
113 | NSColor *fadedColor = [mainColor colorWithAlphaComponent:0.50]; | |
114 | NSRect textDrawRect = [self titleRectForBounds:cellFrame]; | |
115 | ||
116 | NSMutableAttributedString *titleString = [[[NSMutableAttributedString alloc] initWithString:nameString] autorelease]; | |
117 | [titleString setAttributes:rankedStringIsName ? nameAttributes : detailsAttributes range:NSMakeRange(0, [titleString length])]; | |
118 | ||
119 | // Bring out the matched letters | |
120 | if (abbrString && ![abbrString hasPrefix:@"QSActionMnemonic"]) { | |
121 | [titleString addAttribute:NSForegroundColorAttributeName value:rankedStringIsName ? fadedColor : [fadedColor colorWithAlphaComponent:0.8] range:NSMakeRange(0, [titleString length])]; | |
122 | ||
123 | NSUInteger i = 0; | |
124 | NSUInteger j = 0; | |
125 | NSUInteger hits[[titleString length]]; | |
126 | NSUInteger count = [hitMask getIndexes:(NSUInteger *)&hits maxCount:[titleString length] inIndexRange:nil]; | |
127 | NSDictionary *attributes = @{ | |
128 | NSForegroundColorAttributeName: rankedStringIsName ? mainColor : fadedColor | |
129 | }; | |
130 | for(i = 0; i<count; i += j) { | |
131 | for (j = 1; i+j<count && hits[i+j-1] +1 == hits[i+j]; j++); | |
132 | [titleString addAttributes:attributes range:NSMakeRange(hits[i], j)]; | |
133 | } | |
134 | } else { | |
135 | [titleString addAttribute:NSBaselineOffsetAttributeName value:[NSNumber numberWithDouble:-1.0] range:NSMakeRange(0, [titleString length])]; | |
136 | } | |
137 | // | |
138 | // // Ranked string and nameString aren't the same. Show 'nameString ⟷ rankedString' in the UI | |
139 | // if (!rankedStringIsName && [drawObject displayName].length) { | |
140 | // [titleString addAttribute:NSFontAttributeName value:detailsFont range:NSMakeRange(0,[titleString length])]; | |
141 | // NSMutableAttributedString *attributedNameString = [[NSMutableAttributedString alloc] initWithString:[drawObject displayName]]; | |
142 | // [attributedNameString setAttributes:nameAttributes range:NSMakeRange(0, [[drawObject displayName] length])]; | |
143 | // | |
144 | // [attributedNameString appendAttributedString:[[[NSAttributedString alloc] initWithString:@" ⟷ " attributes:rankedNameAttributes] autorelease]]; | |
145 | // // the replaceCharacters... method inserts the new string into the receiver at the start of the work (range.location and range.length are 0) | |
146 | // [titleString replaceCharactersInRange:NSMakeRange(0,0) withAttributedString:attributedNameString]; | |
147 | // [attributedNameString release]; | |
148 | // } | |
149 | ||
150 | if (showDetails) { | |
151 | NSString *detailsString = [drawObject details]; | |
152 | ||
153 | NSRange returnRange = [detailsString rangeOfString:@"\n"]; | |
154 | if (returnRange.location != NSNotFound) { | |
155 | detailsString = [detailsString substringToIndex:returnRange.location]; | |
156 | } | |
157 | ||
158 | detailsAttributes = [detailsAttributes mutableCopy]; | |
159 | [detailsAttributes setValue:[NSColor grayColor] forKey:NSForegroundColorAttributeName]; | |
160 | ||
161 | if (detailsString && detailsString.length && ![detailsString isEqualToString:nameString]) { | |
162 | [titleString appendAttributedString:[[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"\n%@",detailsString] attributes:detailsAttributes] autorelease]]; | |
163 | } | |
164 | } | |
165 | ||
166 | NSRect centerRect = rectFromSize([titleString size]); | |
167 | centerRect.size.width = NSWidth(textDrawRect); | |
168 | centerRect.size.height = MIN(NSHeight(textDrawRect), centerRect.size.height); | |
169 | [titleString drawInRect:centerRectInRect(centerRect, textDrawRect)]; | |
170 | } | |
171 | ||
953623b9 RBR |
172 | - (void)drawSearchPlaceholderWithFrame:(NSRect)cellFrame inView:(NSView *)controlView { |
173 | NSString *defaultText = NSLocalizedStringWithDefaultValue(@"Type to search", nil, [NSBundle mainBundle], @"Type to search", @"Hint that appears in the first pane of the QS interface when it's empty."); | |
174 | NSSize textSize = [defaultText sizeWithAttributes:nameAttributes]; | |
175 | NSRect textRect = centerRectInRect(rectFromSize(textSize), cellFrame); | |
176 | BOOL isFirstResponder = [[controlView window] firstResponder] == controlView && ![controlView isKindOfClass:[NSTableView class]]; | |
177 | ||
178 | if (isFirstResponder && [controlView isKindOfClass:[QSSearchObjectView class]]) { | |
179 | NSImage *find = [NSImage imageWithSystemSymbolName:@"magnifyingglass.circle.fill" accessibilityDescription:nil]; | |
180 | ||
181 | ||
182 | [find setSize:QSSize16]; | |
183 | NSRect findImageRect = expelRectFromRectOnEdge(centerRectInRect(rectFromSize([find size]), cellFrame), textRect, NSRectEdgeMinX, -2); | |
184 | ||
185 | ||
186 | NSGraphicsContext *graphicsContext = [NSGraphicsContext currentContext]; | |
187 | [graphicsContext saveGraphicsState]; | |
188 | CGContextRef context = [graphicsContext CGContext]; | |
189 | CGContextBeginTransparencyLayerWithRect(context, findImageRect, nil); | |
190 | CGContextSetBlendMode(context, kCGBlendModeNormal); | |
191 | [find drawInRect:findImageRect fromRect:rectFromSize([find size]) operation:NSCompositingOperationSourceOver fraction:1]; | |
192 | CGContextSetBlendMode(context, kCGBlendModeSourceIn); | |
193 | CGContextSetFillColorWithColor(context, [[NSColor textColor] CGColor]); | |
194 | CGContextFillRect(context, findImageRect); | |
195 | CGContextEndTransparencyLayer(context); | |
196 | ||
197 | [defaultText drawInRect:textRect withAttributes:nameAttributes]; | |
198 | } | |
199 | } | |
5200836d | 200 | |
953623b9 | 201 | @end |